 /*******************************************************************************
  * Copyright (c) 2000, 2006 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/

 package org.eclipse.ui.internal;

 import java.util.ArrayList ;
 import java.util.HashMap ;
 import java.util.Iterator ;
 import java.util.Map ;

 import org.eclipse.jface.action.ContributionItem;
 import org.eclipse.jface.action.IAction;
 import org.eclipse.jface.action.IContributionItem;
 import org.eclipse.jface.action.IMenuListener;
 import org.eclipse.jface.action.IMenuManager;
 import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.viewers.ISelection;
 import org.eclipse.jface.viewers.ISelectionProvider;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Menu;
 import org.eclipse.swt.widgets.MenuItem;
 import org.eclipse.ui.IEditorPart;
 import org.eclipse.ui.IWorkbenchPage;
 import org.eclipse.ui.IWorkbenchPart;
 import org.eclipse.ui.IWorkbenchWindow;
 import org.eclipse.ui.internal.util.Util;
 import org.eclipse.ui.part.IShowInSource;
 import org.eclipse.ui.part.IShowInTargetList;
 import org.eclipse.ui.part.ShowInContext;
 import org.eclipse.ui.views.IViewDescriptor;
 import org.eclipse.ui.views.IViewRegistry;

 /**
  * A <code>ShowInMenu</code> is used to populate a menu manager with
  * Show In actions. The items to show are determined from the active perspective
  * and active part.
  */
 public class ShowInMenu extends ContributionItem {

     private static final String NO_TARGETS_MSG = WorkbenchMessages.Workbench_showInNoTargets;

     private IWorkbenchWindow window;

     private Map actions = new HashMap (21);

     private boolean dirty = true;

     private IMenuListener menuListener = new IMenuListener() {
         public void menuAboutToShow(IMenuManager manager) {
             manager.markDirty();
             dirty = true;
         }
     };

     /**
      * Creates a Show In menu.
      *
      * @param window the window containing the menu
      */
     public ShowInMenu(IWorkbenchWindow window, String id) {
         super(id);
         this.window = window;
     }

     protected IWorkbenchWindow getWindow() {
         return window;
     }

     public boolean isDirty() {
         return dirty;
     }

     /**
      * Overridden to always return true and force dynamic menu building.
      */
     public boolean isDynamic() {
         return true;
     }

     public void fill(Menu menu, int index) {
         if (getParent() instanceof MenuManager) {
             ((MenuManager) getParent()).addMenuListener(menuListener);
         }

         if (!dirty) {
             return;
         }

         MenuManager manager = new MenuManager();
         fillMenu(manager);
         IContributionItem[] items = manager.getItems();
         if (items.length == 0) {
             MenuItem item = new MenuItem(menu, SWT.NONE, index++);
             item.setText(NO_TARGETS_MSG);
             item.setEnabled(false);
         } else {
             for (int i = 0; i < items.length; i++) {
                 items[i].fill(menu, index++);
             }
         }
         dirty = false;
     }

     /**
      * Fills the menu with Show In actions.
      */
     private void fillMenu(IMenuManager innerMgr) {
         // Remove all.
 innerMgr.removeAll();

         IWorkbenchPart sourcePart = getSourcePart();
         if (sourcePart == null) {
             return;
         }
         ShowInContext context = getContext(sourcePart);
         if (context == null) {
             return;
         }
         if (context.getInput() == null
                 && (context.getSelection() == null || context.getSelection()
                         .isEmpty())) {
             return;
         }

         IViewDescriptor[] viewDescs = getViewDescriptors(sourcePart);
         if (viewDescs.length == 0) {
             return;
         }

         for (int i = 0; i < viewDescs.length; ++i) {
             IAction action = getAction(viewDescs[i]);
             if (action != null) {
                 innerMgr.add(action);
             }
         }
     }

     /**
      * Returns the action for the given view id, or null if not found.
      */
     private IAction getAction(IViewDescriptor desc) {
         // Keep a cache, rather than creating a new action each time,
 // so that image caching in ActionContributionItem works.
 IAction action = (IAction) actions.get(desc.getId());
         if (action == null) {
             if (desc != null) {
                 action = new ShowInAction(window, desc);
                 actions.put(desc.getId(), action);
             }
         }
         return action;
     }

     /**
      * Returns the Show In... target part ids for the given source part.
      * Merges the contributions from the current perspective and the source part.
      */
     private ArrayList getShowInPartIds(IWorkbenchPart sourcePart) {
         ArrayList targetIds = new ArrayList ();
         WorkbenchPage page = (WorkbenchPage) getWindow().getActivePage();
         if (page != null) {
             targetIds.addAll(page.getShowInPartIds());
         }
         IShowInTargetList targetList = getShowInTargetList(sourcePart);
         if (targetList != null) {
             String [] partIds = targetList.getShowInTargetIds();
             if (partIds != null) {
                 for (int i = 0; i < partIds.length; ++i) {
                     if (!targetIds.contains(partIds[i])) {
                         targetIds.add(partIds[i]);
                     }
                 }
             }
         }
         page.sortShowInPartIds(targetIds);
         return targetIds;
     }

     /**
      * Returns the source part, or <code>null</code> if there is no applicable
      * source part
      * <p>
      * This implementation returns the current part in the window.
      * Subclasses may extend or reimplement.
      *
      * @return the source part or <code>null</code>
      */
     private IWorkbenchPart getSourcePart() {
         IWorkbenchPage page = getWindow().getActivePage();
         if (page != null) {
             return page.getActivePart();
         }
         return null;
     }

     /**
      * Returns the <code>IShowInSource</code> provided by the source part,
      * or <code>null</code> if it does not provide one.
      *
      * @param sourcePart the source part
      * @return an <code>IShowInSource</code> or <code>null</code>
      */
     private IShowInSource getShowInSource(IWorkbenchPart sourcePart) {
         return (IShowInSource)Util.getAdapter(sourcePart, IShowInSource.class);
     }

     /**
      * Returns the <code>IShowInTargetList</code> for the given source part,
      * or <code>null</code> if it does not provide one.
      *
      * @param sourcePart the source part
      * @return the <code>IShowInTargetList</code> or <code>null</code>
      */
     private IShowInTargetList getShowInTargetList(IWorkbenchPart sourcePart) {
         return (IShowInTargetList)Util.getAdapter(sourcePart, IShowInTargetList.class);
     }

     /**
      * Returns the <code>ShowInContext</code> to show in the selected target,
      * or <code>null</code> if there is no valid context to show.
      * <p>
      * This implementation obtains the context from the <code>IShowInSource</code>
      * of the source part (if provided), or, if the source part is an editor,
      * it creates the context from the editor's input and selection.
      * <p>
      * Subclasses may extend or reimplement.
      *
      * @return the <code>ShowInContext</code> to show or <code>null</code>
      */
     private ShowInContext getContext(IWorkbenchPart sourcePart) {
         IShowInSource source = getShowInSource(sourcePart);
         if (source != null) {
             ShowInContext context = source.getShowInContext();
             if (context != null) {
                 return context;
             }
         } else if (sourcePart instanceof IEditorPart) {
             Object input = ((IEditorPart) sourcePart).getEditorInput();
             ISelectionProvider sp = sourcePart.getSite().getSelectionProvider();
             ISelection sel = sp == null ? null : sp.getSelection();
             return new ShowInContext(input, sel);
         }
         return null;
     }

     /**
      * Returns the view descriptors to show in the dialog.
      */
     private IViewDescriptor[] getViewDescriptors(IWorkbenchPart sourcePart) {
         String srcId = sourcePart.getSite().getId();
         ArrayList ids = getShowInPartIds(sourcePart);
         ArrayList descs = new ArrayList ();
         IViewRegistry reg = WorkbenchPlugin.getDefault().getViewRegistry();
         for (Iterator i = ids.iterator(); i.hasNext();) {
             String id = (String ) i.next();
             if (!id.equals(srcId)) {
                 IViewDescriptor desc = reg.find(id);
                 if (desc != null) {
                     descs.add(desc);
                 }
             }
         }
         return (IViewDescriptor[]) descs.toArray(new IViewDescriptor[descs
                 .size()]);
     }

 }

